package practice

import (
	"fmt"
)

/*
	//接口:
	接口是一个或多个方法签名的集合。任何类型的方法集中只要拥有该接口“对应的全部方法”签名。就表示它 "实现" 了该接口，无须在该类型上显式声明实现了哪个接口
	对应的全部方法：是指有相同名称、参数列表 (不包括参数名) 以及返回值
	接口只有方法的申明，没有实现
	接口可以匿名嵌入到其他接口，或是嵌入结构体中
	接口不能访问实现类中的属性
*/

type notifier interface {
	notify()
}

type user struct {
	name  string
	email string
}

//作为user struct的方法实现notifier接口
//若以指针类型 *T 实现接口,只有 *T 实现了该接口
func (u *user) notify() {
	fmt.Printf("Sending user email to %s<%s>\n", u.name, u.email)
}

func sendNotificatioin(n notifier) {
	n.notify()
}

func Main_interface_use() {
	/************ 简单接口使用 ************/
	var phone Phone
	phone = new(NokiaPhone)
	phone.call() //I am Nokia, I can call you!
	phone = new(IPhone)
	phone.call() //I am iPhone, I can call you!

	/************ 测试*T实现的接口调用方式 ************/
	u := user{name: "Bill", email: "bill@email.com"}
	sendNotificatioin(&u) //注意(u *user) notify()定义时是指针,所以这里要传地址
}

type Phone interface {
	call()
}

type NokiaPhone struct {
}

//名称与接口中方法相同
func (nokiaPhone NokiaPhone) call() {
	fmt.Println("I am Nokia, I can call you!")
}

type IPhone struct {
}

//名称与接口中方法相同
func (iPhone IPhone) call() {
	fmt.Println("I am iPhone, I can call you!")
}

/*
	//重要
	一.以实体类型实现接口和以指针类型实现接口的区别:
	1.若以值类型 T 实现接口,不管是 T 和 *T 都实现了该接口
	2.若以指针类型 *T 实现接口,只有 *T 实现了该接口

	type Animal interface {
	   say()
	}

	type Dog struct {
	   name string
	}

	type Cat struct {
	   name string
	}

	//Dog的值类型实现了Animal接口. (代表T和*T都实现了该接口)
	func (dogVal Dog) say() {
	   fmt.Println("I am", dogVal.name)
	}

	//Cat的指针类型实现了Animal接口. (代表仅*T实现了该接口)
	func (catPtr *Cat) say() {
	   fmt.Println("I am", catPtr.name)
	}

	func main() {
	   var d1 Animal= Dog{name:"wangCai1"}
	   d1.say() //Dog的值类型实现了Animal接口,正确

	   var d2 Animal= &Dog{name:"wangCai2"}
	   d2.say() //Dog的指针类型实现了Animal接口,正确

	   var c1 Animal= Cat{name:"miaoMiao1"}
	   c1.say() //Cat的指针类型没有实现了Animal接口(仅实现了值类型),运行报错!!!

	   var c2 Animal= &Cat{name:"miaoMiao2"}
	   c2.say() //Cat的指针类型实现了Animal接口,正确
	}
*/

/*
	//重要
	二.值类型(T)嵌入和指针类型(*T)嵌入的区别:
	type Person struct {
	   age  int8
	   name string
	}

	func (s Person) showName() {
	   fmt.Println(s.name)
	}

	func (s *Person) setName(newName string) {
	   s.name = newName
	}

	type Student1 struct {
	   Person 	//值类型嵌入,默认值为{age:0, name:""}. Student1对应的值类型和pointer都包含Person
	}

	type Student2 struct {
	   *Person	//指针类型的嵌入,默认值为nil
	}

	s1 := Student1{}			//其内嵌Person默认值为{age:0, name:""}
	s1.setName("student1_01") 	// ok
	s1.showName()

	s2 := &Student2{}			//其内嵌Person*默认值为nil
	s2.setName("student1_02") 	//报错!! 因为目前内嵌类型的值为nil
	s2.showName()

	//可通过给嵌入类型赋值解决上面错误
	s3 := &Student2{Person:&Person{age:3, name:"s3"}}
	s3.showName()
*/

/*
	//重要
	三.接口中嵌套和方法集的关系:
	1.类型 S 包含匿名字段 T，则 S和*S 方法集包含 T 方法
	2.类型 S 包含匿名字段 *T，则 S和 *S 方法集包含 T + *T 方法
	3.不管嵌入的是T还是*T，*S方法集，包含 T + *T 方法

	type Human interface {
	   showName()
	   setName(string)
	}

	type Person struct {
	   age  int8
	   name string
	}
	func (s Person) showName() { 				//Person和*Person都实现了Human的showName方法
	   fmt.Println(s.name)
	}
	func (s *Person) setName(newName string) {	//只有*Person实现了Human的setName方法
	   s.name = newName
	}

	type Student1 struct {
	   Person	//值类型嵌入
	}

	type Student2 struct {
	   *Person	//指针类型嵌入
	}

	func main(){
		// Error 应用上面的关系判断第1条规则，因为Student1实例类型的方法集中，仅仅包含Person的实例方法集，即仅仅包含showName()方法，所以Student1的实例类型，没有实现Human接口
		var s1 Human = Student1{} //报错:Student1 does not implement Human (setName method has pointer receiver)
		s1.setName("student1_01")
		s1.showName()

		var s2 Human = &Student1{} //ok 应用第1条和弟3条规则
		s2.setName("student1_02")
		s2.showName()

		var s3 Human = Student2{&Person{}} //ok ，应用第2条规则
		s3.setName("student2_01")
		s3.showName()

		var s4 Human = &Student2{&Person{}} //ok ，应用第2条规则
		s4.setName("student2_02")
		s4.showName()
	}
*/
